﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using Apollo.AST;
using Apollo.TokenUtils;

namespace Apollo.LexerUtils
{
    /// <summary>
    /// 词法分析器
    /// </summary>
    internal class Lexer
    {
        private string  m_file;     // 当前代码文件名
        private string  m_input;    // 当前代码
        private char    m_ch;       // 当前字符
        private int     m_pos;      // 当前字符的位置
        private int     m_next_pos; // 下一个字符位置

        private int     m_line;     // 当前字符的行号
        private int     m_column;   // 当前字符的列号

        /// <summary>
        /// 默认构造函数
        /// </summary>
        public Lexer()
        {
            
        }

        /// <summary>
        /// 参数构造函数
        /// </summary>
        public Lexer(string sContent)
        {
            m_ch = '\0';
            m_pos = 0;
            m_next_pos = 0;
            m_input = sContent;

            m_line = 1;
            m_column = 0;
        }

        /// <summary>
        /// 参数构造函数
        /// </summary>
        /// <param name="sFileName">代码文件名称</param>
        public void LoadFromFile(string sFileName)
        {
            m_file = sFileName;
            m_ch = '\0';
            m_pos = 0;
            m_next_pos = 0;

            if (File.Exists(sFileName))
            {
                m_input = File.ReadAllText(sFileName);
                if (m_input.Length > 0)
                {
                    m_ch = m_input[0];
                    return;
                }
                else
                {
                    throw new Exception("File length is zero");
                }
            }

            throw new Exception("File open failure!");
        }

        /// <summary>
        /// 生成下一个Token, 关键函数
        /// </summary>
        /// <returns></returns>
        public Token Next_Token()
        {
            Read_Char();
            Skip_White_Space();

            string literal = "";
            literal += m_ch;

            switch (m_ch)
            {
                #region -----------——- 赋值运算符 --------------------
                case '=':
                    if (Peek_Char() == '=')
                    {
                        Read_Char();
                        return New_Token(TokenType.EQ, "==");
                    }
                    else
                    {
                        return New_Token(TokenType.ASSIGN, "=");
                    }
                #endregion

                #region ------------ 算术运算符 ----------------------
                case '+':
                    if (Peek_Char() == '=')
                    {
                        Read_Char();
                        return New_Token(TokenType.PLUS_ASSIGN, "+=");
                    }
                    else if (Peek_Char() == '+')
                    {
                        Read_Char();
                        return New_Token(TokenType.PLUS_PLUS, "++");
                    }
                    else
                    {
                        return New_Token(TokenType.PLUS, "+");
                    }
                case '-':
                    if (Peek_Char() == '=')
                    {
                        Read_Char();
                        return New_Token(TokenType.MINUS_ASSIGN, "-=");
                    }
                    else if (Peek_Char() == '-')
                    {
                        Read_Char();
                        return New_Token(TokenType.MINUS_MINUS, "--");
                    }
                    else
                    {
                        return New_Token(TokenType.MINUS, "-");
                    }
                case '*':
                    if (Peek_Char() == '=')
                    {
                        Read_Char();
                        return New_Token(TokenType.ASTERISK_ASSIGN, "+=");
                    }
                    if (Peek_Char() == '*')
                    {
                        Read_Char();
                        return New_Token(TokenType.ASTERISK_ASSTERISK, "**");
                    }
                    else
                    {
                        return New_Token(TokenType.ASTERISK, "*");
                    }
                case '/':
                    if (Peek_Char() == '=')
                    {
                        Read_Char();
                        return New_Token(TokenType.SLASH_ASSIGN, "/=");
                    }
                    else
                    {
                        return New_Token(TokenType.SLASH, "/");
                    }
                case '%':
                    if (Peek_Char() == '=')
                    {
                        Read_Char();
                        return New_Token(TokenType.MODULO_ASSIGN, "%=");
                    }
                    else
                    {
                        return New_Token(TokenType.MODULO, "%");
                    }
                #endregion

                #region ------------ 比较运算符 ----------------------
                case '>':
                    if (Peek_Char() == '=')
                    {
                        Read_Char();
                        return New_Token(TokenType.GE, ">=");
                    }
                    else
                    {
                        return New_Token(TokenType.GT, ">");
                    }
                case '<':
                    if (Peek_Char() == '=')
                    {
                        Read_Char();
                        return New_Token(TokenType.LE, "<=");
                    }
                    else
                    {
                        return New_Token(TokenType.LT, "<");
                    }
                case '!':
                    if (Peek_Char() == '=')
                    {
                        Read_Char();
                        return New_Token(TokenType.NE, "!=");
                    }
                    else
                    {
                        return New_Token(TokenType.NOT, "!");
                    }
                #endregion

                #region ------------ 逻辑运算符 ----------------------
                case '&':
                    if (Peek_Char() == '=')
                    {
                        Read_Char();
                        return New_Token(TokenType.BIT_AND_ASSIGN, "&=");
                    }
                    else if (Peek_Char() == '&')
                    {
                        Read_Char();
                        return New_Token(TokenType.AND, "&&");
                    }
                    else
                    {
                        return New_Token(TokenType.BIT_AND, "&");
                    }
                case '|':
                    if (Peek_Char() == '=')
                    {
                        Read_Char();
                        return New_Token(TokenType.BIT_OR_ASSIGN, "|=");
                    }
                    else if (Peek_Char() == '|')
                    {
                        Read_Char();
                        return New_Token(TokenType.OR, "||");
                    }
                    else
                    {
                        return New_Token(TokenType.BIT_OR, "|");
                    }
                case '^':
                    if (Peek_Char() == '=')
                    {
                        Read_Char();
                        return New_Token(TokenType.BIT_XOR_ASSIGN, "^=");
                    }
                    else
                    {
                        return New_Token(TokenType.BIT_XOR, "^");
                    }
                #endregion

                #region ------------ 符号提取   ----------------------
                case '(':
                    return New_Token(TokenType.LPAREN, literal);
                case ')':
                    return New_Token(TokenType.RPAREN, literal);
                case '[':
                    return New_Token(TokenType.LBRACKET, literal);
                case ']':
                    return New_Token(TokenType.RBRACKET, literal);
                case '{':
                    return New_Token(TokenType.LBRACE, literal);
                case '}':
                    return New_Token(TokenType.RBRACE, literal);
                case '.':
                    return New_Token(TokenType.DOT, literal);
                case '~':
                    return New_Token(TokenType.TILDE, literal);
                case '@':
                    return New_Token(TokenType.AT, literal);
                case ',':
                    return New_Token(TokenType.COMMA, literal);
                case ';':
                    return New_Token(TokenType.SEMICOLON, literal);
                case ':':
                    return New_Token(TokenType.COLON, literal);
                case '?':
                    return New_Token(TokenType.QUESTION, literal);
                case '\0':
                    return New_Token(TokenType.EOF, "end");
                #endregion

                default:
                    if (Char.IsDigit(m_ch))         // 是整数
                    {
                        bool bFloat = false;
                        string sNumber = Read_Integer_Float(ref bFloat);
                        Unread_Char();
                        if (bFloat)
                            return New_Token(TokenType.FLOAT, sNumber);
                        else
                            return New_Token(TokenType.INTEGER, sNumber);
                    }
                    else if (Char.IsLetter(m_ch))   // 是字符
                    {
                        string sIdentifer = Read_Identifer();
                        Unread_Char();
                        if (Token.KeywordsDict.TryGetValue(sIdentifer, out var type))
                        {
                            return New_Token(type, sIdentifer);
                        }
                        return New_Token(TokenType.IDENTIFIER, sIdentifer);
                    }
                    else if (m_ch == '"')       // 字符串
                    {
                        string s = Read_String();
                        Unread_Char();
                        Read_Char();
                        return New_Token(TokenType.STRING, s);
                    }
                    else
                        return New_Token(TokenType.ILLEGAL, literal);
            }
        }

        #region 实用私有方法
        private void Skip_White_Space()
        {
            while (m_ch == ' ' || m_ch == '\t' || m_ch == '\r' || m_ch == '\n')
            {
                if (m_ch == '\n')
                {
                    m_line += 1;
                    m_column = 0;
                }
                else if (m_ch == ' ')
                {
                    m_column += 1;
                }
                else if (m_ch == '\t')
                {
                    m_column += 4;
                }
                Read_Char();
            }
        }

        /// <summary>
        /// 读取字符
        /// </summary>
        private void Read_Char()
        {
            if (m_next_pos >= m_input.Length)
            {
                m_ch = '\0';
            }
            else
            {
                m_ch = m_input[m_next_pos];
            }
            m_pos = m_next_pos;
            m_next_pos++;
            m_column++;
        }

        /// <summary>
        /// 查找下一个字符
        /// </summary>
        /// <returns></returns>
        private char Peek_Char()
        {
            if (m_next_pos < m_input.Length)
                return m_input[m_next_pos];
            return '\0';
        }

        /// <summary>
        /// 调整读取位置
        /// </summary>
        private void Unread_Char()
        {
            m_next_pos = m_pos;
            m_pos--;
            m_column--;
        }

        /// <summary>
        /// 读取整数
        /// </summary>
        /// <returns></returns>
        private string Read_Integer_Float(ref bool bFloat)
        {
            int pos = m_pos;            
            while (char.IsDigit(m_ch) || m_ch == '.' || m_ch == 'e' || m_ch == 'E')
            {
                if (m_ch == '.' || m_ch == 'e' || m_ch == 'E')
                {
                    bFloat = true;
                }
                Read_Char();
            }
            return m_input.Substring(pos, m_pos - pos);
        }
        
        /// <summary>
        /// 读取标识符
        /// </summary>
        /// <returns></returns>
        private string Read_Identifer()
        {
            int pos = m_pos;
            while (char.IsLetter(m_ch) || char.IsDigit(m_ch) || m_ch == '_' )
            {
                Read_Char();
            }
            return m_input.Substring(pos, m_pos - pos);
        }

        /// <summary>
        /// 读取字符串
        /// </summary>
        /// <returns></returns>
        private string Read_String()
        {
            Read_Char();
            int pos = m_pos;
            while (m_ch != '"' && m_ch != '\0')
            {
                Read_Char();
            }
            return m_input.Substring(pos, m_pos - pos);
        }
        
        private Token New_Token(TokenType type, string literal) 
        {
            var token = new Token(type, literal, m_line, m_column);
            return token;
        }
        #endregion
    }
}
